﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "UiInplaceEditModeUtil.h"
#include "InplaceEditSharedContent.h"
#include "GmProcessServer.h"
#include "InplaceEditProcessChatServer.h"
#include "UiInplaceEditModeProjectUtil.h"
#include "SocketPortConfig.h"
#include "GmCommunicationProtocolUtils.h"
#include "ComponentEditProjectState.h"

// 平台头文件
#include "IModelView.h"
#include "IDocument.h"
#include "IElementBasicInformation.h"
#include "IGraphicsNodePathCollector.h"
#include "IGraphicsElementShapeReference.h"
#include "IElementModelShape.h"
#include "IGraphicsNodeGroup.h"
#include "ILinkElementProxy.h"

#include "IUiDocument.h"
#include "IUiDocumentViewManager.h"
#include "IUiView.h"
#include "ICamera.h"
#include "FilePath.h"
#include "ISelection.h"
#include "IGraphicsNodeReference.h"
#include "IGraphicsStyle.h"
#include "IGraphicsMaterial.h"
#include "GbmpFileSystem.h"

#include "JournalUtils.h"
#include "IJournalClient.h"
#include "FilePathManager.h"
#include "Vector3dUtils.h"
#include "InplaceEditProjectJournalMessageSender.h"
#include "ICanvas.h"
#include "IGenericElement.h"
#include "IFillPattern.h"
#include "ILineTypeNew.h"
#include "GcmpBuiltInCategoryUniIdentities.h"

#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"


using namespace gcmp;

namespace
{
    class GReferenceTester : public gcmp::IGraphicsNodePathTester
    {
    public:
        virtual bool IsDesiredGraphicsNodePath(const IGraphicsNodePath& nodePath) const override
        {
            if (!nodePath.isValid() || nodePath.GetPath().empty())
            {
                return false;
            }

            // 检查子节点是否有GReference
            const IGraphicsGroupBase* pGroup = quick_cast<IGraphicsGroupBase>(nodePath.GetPath().back().Get());
            if (!pGroup)
            {
                return false;
            }
            for (int i = 0; i < pGroup->GetChildrenCount(); ++i)
            {
                const IGraphicsNode* pChild = pGroup->GetChild(i);
                if (pChild && quick_cast<IGraphicsElementShapeReference>(pChild))
                {
                    return true;
                }
            }

            return false;
        }
    };

    class GReferenceProcessor : public IGraphicsNodeProcessor
    {
    private:
        IDocument *m_pDoc;
    public:
        GReferenceProcessor(IDocument *pDoc) :m_pDoc(pDoc) {}

        OwnerPtr<IGraphicsNode> GetRealNode(const IGraphicsElementShapeReference* pReference)
        {
            DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pReference, L"pReference为空",L"GDMPLab",L"2024-03-30");
            DBG_WARN_AND_RETURN_NULLPTR_UNLESS(m_pDoc, L"m_pDoc为空",L"GDMPLab",L"2024-03-30");

            IElement* pRefElement = m_pDoc->GetElement(pReference->GetReferencedElementId());
            DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pRefElement, L"pRefElement为空",L"GDMPLab",L"2024-03-30");
            const IElementModelShape* pModelShape = pRefElement->GetElementModelShape();
            DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pModelShape, L"pModelShape为空",L"GDMPLab",L"2024-03-30");
            const IGraphicsElementShape* pGrep = pModelShape->GetGraphicsElementShape();
            DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pGrep, L"GrepHolder里面形体为空",L"GDMPLab",L"2024-03-30");

            OwnerPtr<IGraphicsNodeGroup> opGroup = IGraphicsNodeGroup::Create();
            opGroup->SetId(pReference->GetId());
            for (int i = 0; i < pGrep->GetChildrenCount(); ++i)
            {
                opGroup->AddChild(pGrep->GetChild(i)->Clone());
            }
            if (!pReference->GetTransformMatrix().IsIdentity())
            {
                opGroup->Transform(pReference->GetTransformMatrix());
            }

            return TransferOwnership(opGroup);
        }

        virtual void Process(IGraphicsNode* pGraphicsNode) override
        {
            // 替换子节点的GReference
            IGraphicsGroupBase* pGroup = quick_cast<IGraphicsGroupBase>(pGraphicsNode);
            if (!pGroup)
            {
                return;
            }
            for (int i = 0; i < pGroup->GetChildrenCount(); ++i)
            {
                const IGraphicsNode* pChild = pGroup->GetChild(i);
                if (pChild && quick_cast<IGraphicsElementShapeReference>(pChild))
                {
                    OwnerPtr<IGraphicsNode> opRealNode = GetRealNode(quick_cast<IGraphicsElementShapeReference>(pChild));
                    DBG_WARN_AND_CONTINUE_UNLESS(opRealNode, L"获取引用节点失败",L"GDMPLab",L"2024-03-30");
                    pGroup->GetChildrenFw()[i] = TransferOwnership(opRealNode);
                }
            }
        }
    };

    // 通过在位编辑的插入点变换底图形体的位置
    bool TransformByInsertPoint(IGraphicsElementShape* pGrep)
    {
        // 获取插入点
        InplaceEditSharedContent* pInplaceEditSharedContent = ComponentEditProjectState::Get()->GetInplaceEditSharedContent();
        DBG_WARN_AND_RETURN_FALSE_UNLESS(pInplaceEditSharedContent, L"获取在位编辑插入点信息失败",L"GDMPLab",L"2024-03-30");
        Vector3d insertPoint = pInplaceEditSharedContent->GetInsertionPoint();
        Box3d box = pGrep->GetBox();
        Matrix4d transform;
        transform.MakeTranslate(-insertPoint);
        pGrep->Transform(transform);

        // box需要更新下
        box.Translate(-insertPoint);
        pGrep->SetBox(box);

        return true;
    }

    OwnerPtr<InplaceEditSharedElementInfo> GetSharedElementInfo(IDocument* pDoc, const IGraphicsElementShape* pGrep)
    {
        IElement* pElement = pDoc->GetElement(pGrep->GetElementId());
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pElement, L"形体对应的构件不存在",L"GDMPLab",L"2024-03-30");
        UniIdentity categoryUid = pElement->GetBasicInformation()->GetCategoryUid();

        // 获取形体，如果有GReference共享，需要把共享去掉
        OwnerPtr<IGraphicsElementShape> opGrep = TransferOwnershipCast<IGraphicsElementShape>(pGrep->Clone());
        OwnerPtr<IGraphicsNodePathCollector> opCollector = IGraphicsNodePathCollector::Create(pDoc, opGrep.get());
        OwnerPtr<GReferenceTester> opTester = NEW_AS_OWNER_PTR(GReferenceTester);
        OwnerPtr<GReferenceProcessor> opProcessor = NEW_AS_OWNER_PTR(GReferenceProcessor, pDoc);
        opCollector->ProcessGraphicsNodes(opTester.get(), opProcessor.get());

        // 通过在位编辑的插入点变换底图形体的位置
        TransformByInsertPoint(opGrep.get());

        // 创建在位编辑传输对象
        OwnerPtr<InplaceEditSharedElementInfo> opElementInfo = NEW_AS_OWNER_PTR(InplaceEditSharedElementInfo);
        opElementInfo->SetCategoryUid(categoryUid);
        opElementInfo->SetGRep(TransferOwnership(opGrep));

        return opElementInfo;
    }

    // 获取相机参数
    OwnerPtr<InplaceEditSharedCameraInfo> GetCameraInfo(IModelView* pModelView)
    {
        IUiDocumentViewManager* pDocViewManger = IUiDocumentViewManager::Get();
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pDocViewManger, L"pDocViewManger不能为空",L"GDMPLab",L"2024-03-30");
        IUiDocument* pUIDoc = pDocViewManger->GetUiDocumentByName(pModelView->GetDocument()->GetName());
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pUIDoc, L"pUIDoc不能为空",L"GDMPLab",L"2024-03-30");

        // 找到对应已打开的视图
        IUiView* pUiView = nullptr;
        std::vector<IUiView*> uiViews = pUIDoc->GetAllUiViews();
        FOR_EACH(pUiViewItem, uiViews)
        {
            if (pUiViewItem->GetModelViewId() == pModelView->GetElementId())
            {
                pUiView = pUiViewItem;
                break;
            }
        }
        if (!pUiView) // 视图未打开过，没有相机数据
        {
            return nullptr;
        }

        // 获取相机数据
        ICanvas *pCanvas = pUiView->GetCanvas();
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pCanvas != nullptr, L"pCanvas为空",L"GDMPLab",L"2024-03-30");
        ICamera* pCamera = pCanvas->GetCamera();
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pCamera, L"视图相机不能为空",L"GDMPLab",L"2024-03-30");
        CameraType type = CameraType::Ortho;
        Vector3d position, direction, upDirection;
        double aspectRatio = 0, nearDistance = 0, farDistance = 0, perspectiveFieldOfView = 0, orthoWindowHeight = 0;
        pCamera->GetAllParameters(type, position, direction, upDirection
            , aspectRatio, nearDistance, farDistance, perspectiveFieldOfView, orthoWindowHeight);

        // 相机的位置需要根据插入点平移
        InplaceEditSharedContent* pInplaceEditSharedContent = ComponentEditProjectState::Get()->GetInplaceEditSharedContent();
        DBG_WARN_AND_RETURN_FALSE_UNLESS(pInplaceEditSharedContent, L"获取在位编辑插入点信息失败",L"GDMPLab",L"2024-03-30");
        Vector3d insertPoint = pInplaceEditSharedContent->GetInsertionPoint();
        position -= insertPoint;

        OwnerPtr<InplaceEditSharedCameraInfo> opCameraInfo = NEW_AS_OWNER_PTR(InplaceEditSharedCameraInfo);
        opCameraInfo->SetAspectRatio(aspectRatio);
        opCameraInfo->SetDirection(direction);
        opCameraInfo->SetFarDist(farDistance);
        opCameraInfo->SetFieldOfView(perspectiveFieldOfView);
        opCameraInfo->SetNearDist(nearDistance);
        opCameraInfo->SetOrthoWindowHeight(orthoWindowHeight);
        opCameraInfo->SetPosition(position);
        opCameraInfo->SetType((int)type);
        opCameraInfo->SetUpDirection(upDirection);

        return opCameraInfo;
    }
}

gcmp::OwnerPtr<gcmp::InplaceEditSharedViewContent> UiInplaceEditModeProjectUtil::GetSharedViewContent(IDocument* pDoc
    , const InplaceEditSharedModelViewInfo& viewBrief)
{
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pDoc, L"文档参数为空",L"GDMPLab",L"2024-03-30");
    OwnerPtr<gcmp::InplaceEditSharedViewContent> opSharedViewContent = NEW_AS_OWNER_PTR(InplaceEditSharedViewContent);
    opSharedViewContent->SetViewName(viewBrief.GetViewName());

    // 获取相应的视图
    IModelView* pModelView = nullptr;
    std::vector<IModelView*> modelViews = IModelView::GetAllModelViews(pDoc);
    FOR_EACH(pViewItem, modelViews)
    {
        if (pViewItem && pViewItem->GetUserDataId() == viewBrief.GetViewType() && pViewItem->GetName() == viewBrief.GetViewName())

        {
            pModelView = pViewItem;
            break;
        }
    }
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pModelView, L"项目中未找到相应的视图："+ viewBrief.GetViewName(),L"GDMPLab",L"2024-03-30");

    // 相机参数
    OwnerPtr<InplaceEditSharedCameraInfo> opCameraInfo = GetCameraInfo(pModelView);
    opSharedViewContent->SetCameraInfo(TransferOwnership(opCameraInfo));

    // 获取视图GRep，1)过滤掉当前选择集中的构件，2)过滤掉链接对象
    std::vector<const IGraphicsElementShape*> greps = pModelView->GenerateModelViewElementShapes();
    
    ElementId selectedElementId;
    const std::set<OwnerPtr<IGraphicsNodeReference>, OwnerPtrContainerUtil::LessPredicate<IGraphicsNodeReference> >& selectedObjs
        = ISelection::Get()->GetGraphicsNodeReferences();
    if (selectedObjs.size() == 1)
    {
        selectedElementId = (*selectedObjs.begin())->GetElementId();
    }
    FOR_EACH(pGrep, greps)
    {
        ElementId elementIdOfGRep = pGrep->GetElementId();
        if (elementIdOfGRep != selectedElementId)
        {
            // 过滤掉链接对象
            if (nullptr == quick_cast<ILinkElementProxy>(pDoc->GetElement(elementIdOfGRep)))
            {
                OwnerPtr<InplaceEditSharedElementInfo> opSharedElementInfo = GetSharedElementInfo(pDoc, pGrep);
                if (opSharedElementInfo)
                {
                    opSharedViewContent->GetSharedElementInfosFW().push_back(TransferOwnership(opSharedElementInfo));
                }
            }
        }
    }

    // 获取引用的材质样式对象
    std::set<ElementId> styleIds;
    std::set<ElementId> materialIds;
    std::vector<ElementId> referenceIds;
    FOR_EACH(grepInfo, opSharedViewContent->GetSharedElementInfos())
    {
        const IGraphicsElementShape* pGrep = grepInfo->GetGRep();
        referenceIds.clear();
        pGrep->GetReferencedElementIds(referenceIds);
        FOR_EACH(id, referenceIds)
        {
            const IElement* pElement = pDoc->GetElement(id);
            if (pElement)
            {
                if (quick_cast<IGraphicsStyle>(pElement))
                {
                    styleIds.insert(id);
                }
                else if (quick_cast<IGraphicsMaterial>(pElement))
                {
                    materialIds.insert(id);
                }
            }
        }
    }

    // 添加材质样式数据
    FOR_EACH(id, styleIds)
    {
        const IGraphicsStyle* pStyle = quick_cast<IGraphicsStyle>(pDoc->GetElement(id));
        OwnerPtr<IGraphicsStyleData> opStyleData = pStyle->GetGraphicsStyleData();
        DBG_WARN_AND_CONTINUE_UNLESS(opStyleData, L"样式数据为空",L"GDMPLab",L"2024-03-30");
        opSharedViewContent->AddStyleInfo(id, opStyleData.get());
    }
    FOR_EACH(id, materialIds)
    {
        const IGraphicsMaterial* pMaterial = quick_cast<IGraphicsMaterial>(pDoc->GetElement(id));
        OwnerPtr<IGraphicsMaterialData> opMaterialData = pMaterial->GetGraphicsMaterialDataFromMaterialInstance();
        DBG_WARN_AND_CONTINUE_UNLESS(opMaterialData, L"样式数据为空",L"GDMPLab",L"2024-03-30");
        opSharedViewContent->AddMaterialInfo(id, opMaterialData.get());
    }
    //添加填充样式数据
    std::vector<IElement*> HatchPatternElements = pDoc->GetElementsByCategory(BuiltInCategoryUniIdentities::BICU_HATCH_PATTERN);
    FOR_EACH(pHatchPatternElement, HatchPatternElements)
    {
        IGenericElement* pHatchPatternElem = quick_cast<IGenericElement>(pHatchPatternElement);
        if (!pHatchPatternElem)
            continue;

        IFillPattern* pHatchPatternObject = quick_cast<IFillPattern>(pHatchPatternElem->GetExternalObject());
        DBG_WARN_AND_CONTINUE_UNLESS(pHatchPatternObject, L"pHatchPatternObject为空",L"GDMPLab",L"2024-03-30");

        opSharedViewContent->AddFillPatternInfo(pHatchPatternElem->GetElementId(), pHatchPatternObject->GetFillPatternData());
    }
    //添加线型数据
    std::vector<IElement*> LineTypeElements = pDoc->GetElementsByCategory(BuiltInCategoryUniIdentities::BICU_LINE_TYPE);
    FOR_EACH(pLineTypeElement, LineTypeElements)
    {
        IGenericElement* pLineTypeEle = quick_cast<IGenericElement>(pLineTypeElement);
        if (!pLineTypeEle) 
            continue;

        ILineTypeNew* pLineTypeObject = quick_cast<ILineTypeNew>(pLineTypeEle->GetExternalObject());
        DBG_WARN_AND_CONTINUE_UNLESS(pLineTypeObject, L"pLineTypeObject为空",L"GDMPLab",L"2024-03-30");

        opSharedViewContent->AddLineTypeNewInfo(pLineTypeEle->GetElementId(), pLineTypeObject->GetLineTypeData());
    }

    return opSharedViewContent;
}

bool UiInplaceEditModeProjectUtil::StartInplaceEditProject(gcmp::IDocument* pDoc, OwnerPtr<InplaceEditSharedContent> opInplaceEditSharedContent, bool isCreating)
{
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pDoc, L"文档参数为空",L"GDMPLab",L"2024-03-30");

    // 启动族App
    uint16_t port = ProcessServer::GetInstance()->GetPort();
    opInplaceEditSharedContent->SetProjectAppPort(port);

    if (JournalUtils::IsInReplay())
    {
        // 回放时会有单独的（非全局唯一）的端口配置
        SocketPortConfigRecord appRecord(ProcessServer::GetInstance()->GetServerGuid(), port, UiInplaceEditModeUtil::GetAppName());
        SocketPortConfig::CreateJournalReplayEnvironment(appRecord);
    }

    // 检查族App是否正在运行
    std::vector<SocketPortConfigRecord> records;
    bool isOk = SocketPortConfig::GetAppRecords(UiInplaceEditModeUtil::GetFamilyEditorAppName(), records);
    DBG_WARN_AND_RETURN_FALSE_UNLESS(isOk, L"获取族端口记录失败",L"GDMPLab",L"2024-03-30");

    if (records.empty())
    {
        // 启动族App
        std::wstring familyAppPath = L"\"" + FileSystem::GetExeDirPath() + UiInplaceEditModeUtil::GetFamilyEditorAppName()
            + L".exe\" -ReadyRecieverPort" + StringUtil::ToWString(port) + L" -ReadyRecieverFunctionType" + StringUtil::ToWString((int)FunctionType::InPlaceEdit);

        if (JournalUtils::IsInReplay())
        {
            // 回放时允许弹出多个构件编辑器
            familyAppPath += L" -IsNotSingleton -InReplay";
            // 回放时将构件编辑器脚本和主进程脚本放在一起
            std::wstring logsFolderPath = FilePathManager::Get()->GetLogsFolderPath().GetFullPath();
            std::wstring logFolderPath = LogManager::Get()->GetLogsFolder();
            std::wstring logFolderName = logFolderPath.substr(int(logsFolderPath.length()), GbmpString::npos);// 这里拿到的其实是相对于“Log”目录的路径
            familyAppPath += L" /LogFolderName:" + logFolderName;
        }
        else
        {
            familyAppPath += L" -ForceSingleton";
        }
        
        STARTUPINFO si = { sizeof(STARTUPINFO) };
        OwnerPtr<PROCESS_INFORMATION> opProcess = NEW_AS_OWNER_PTR(PROCESS_INFORMATION);
        BOOL hasProcess = ::CreateProcess(NULL, (wchar_t *)familyAppPath.data(), NULL, NULL, FALSE, 0, NULL,
            NULL, &si, opProcess.get());
        DBG_WARN_AND_RETURN_FALSE_UNLESS(hasProcess, L"进程启动失败",L"GDMPLab",L"2024-03-30");

    // 冻结项目App
    InplaceEditProcessChatServer& chatServer = InplaceEditProcessChatServer::GetInstance();
    chatServer.Start(TransferOwnership(opProcess));

        ComponentEditProjectState::Get()->SetInplaceEditSharedContent(TransferOwnership(opInplaceEditSharedContent));

    // 标记正在创建
        ComponentEditProjectState::Get()->SetIsCreating(isCreating);
    }
    else
    {
        // 冻结项目App
        InplaceEditProcessChatServer& chatServer = InplaceEditProcessChatServer::GetInstance();
        chatServer.SetFamilyPort(records.front().m_port);
        chatServer.Start(nullptr);

        ComponentEditProjectState::Get()->SetInplaceEditSharedContent(TransferOwnership(opInplaceEditSharedContent));

        // 标记正在创建
        ComponentEditProjectState::Get()->SetIsCreating(isCreating);

        ComponentEditProjectState::Get()->SetFamilyPort(records.front().m_port);
        CommunicationProtocolUtils::SendIpcMessage(records.front().m_port, FunctionType::InPlaceEdit, OperationType::StartEdit, ComponentEditProjectState::Get()->GetInplaceEditSharedContent());

        OwnerPtr<InplaceEditProjectJournalMessageSender> opInplaceEditProjectJournalHandler = NEW_AS_OWNER_PTR(InplaceEditProjectJournalMessageSender, ComponentEditProjectState::Get()->GetFamilyPort());
        IJournalClient::Get()->SetJournalMessageSender(TransferOwnership(opInplaceEditProjectJournalHandler));
        IJournalClient::Get()->RequestCooperation();
    }

    return true;
}

bool UiInplaceEditModeProjectUtil::GetViewList(gcmp::IDocument* pDoc, const gcmp::Vector3d &insertPoint
    , std::vector<OwnerPtr<gcmp::InplaceEditSharedModelViewInfo>> &viewList)
{
    std::vector<IModelView *> modelViews = IModelView::GetAllModelViews(pDoc);

    Matrix4d transform;
    transform.MakeTranslate(-insertPoint);

    FOR_EACH(pModelView, modelViews)
    {
        if (pModelView)
        {
            OwnerPtr<InplaceEditSharedModelViewInfo> opViewInfo = NEW_AS_OWNER_PTR(InplaceEditSharedModelViewInfo, pModelView->GetUserDataId(), pModelView->GetName(), pModelView->GetViewType());

            opViewInfo->SetOrigin(Vector3dUtils::TransformPoint(pModelView->GetOrigin(), transform));
            opViewInfo->SetRightDirection(pModelView->GetRightDirection());
            opViewInfo->SetUpDirection(pModelView->GetUpDirection());
            opViewInfo->SetViewDirection(pModelView->GetViewDirection());
            OwnerPtr<IPlane> opWorkPlane = pModelView->GetWorkPlane();
            opWorkPlane->Transform(transform);
            opViewInfo->SetWorkPlane(TransferOwnership(opWorkPlane));

            viewList.push_back(TransferOwnership(opViewInfo));
        }
    }

    return true;
}